Data Story: Het verband tussen de levenstandaarden in een land en migratie#

  1. Lars van der Groep 14346168

  2. Guido Elzer 15817628

  3. Simon van Rooij 15692221

Inleiding#

Migratie is al erg lang een erg belangrijk onderwerp waar de meningen verdeeld over zijn. Er zijn namelijk verschillende redenen waardoor mensen uit hun herkomst land vertrekken. Veel mensen en ook overheden zijn geen voorstander van buitenlanders die binnenkomen in hun land, omdat ze gebruik willen maken van het succes dat daar behaald is. In deze datastory wordt er onderzocht of mensen vooral emigreren om uit hun eigen land weg te gaan, om naar een beter land te gaan, of misschien wel een combinatie tussen de twee.

Eerste perspectief:#

Migranten emigreren vooral uit hun eigen land omdat de levensomstandigheden in hun eigen land te slecht/gevaarlijk zijn om een normaal leven in te kunnen hebben.

Argument 1: Landen met een historische gebeurtenis wat de leefomstandigheden onleefbaar maakt hebben een sterke stijging van migratie als gevolg.#

Argument 2: Er is sprake van een zichtbaar verband tussen migratiecijfers een slechte leefomstandigheden maar een slechte leefomstandigheid heeft niet altijd een verhoging van migratie als gevolg.#

Tweede perspectief:#

Migratie heeft niet per se te maken met hoe slecht het met een land gaat maar eerder met hoe goed het met de buurlanden gaat.

Eerste argument: Mortality Rate en Migratie#

Tweede argument: gemiddeld BBP per persoon en emigratiepercentage trendline#

Derde argument: buurlandverschillen#

import pandas as pd
import plotly.express as px
import plotly.graph_objs as go
from plotly.offline import init_notebook_mode
from plotly.subplots import make_subplots
init_notebook_mode(connected=True)
from IPython.display import display, HTML
import json
import uuid

Dataset en Preprocessing#

Om onze argumenten te onderbouwen gebruiken we meerdere datasets die onze punten versterken. De dataset die centraal achter dit onderzoek staat is de dataset over wereldwijde migratie van de World Bank Group(2025) Deze dataset heeft meerdere variabelen die aangegeven zijn in de output hieronder. Alle jaartallen gebruiken data verzameld over een periode van 10 jaar. (Link naar de database https://databank.worldbank.org/source/global-bilateral-migration)

file = r"P_Data_Extract_From_Global_Bilateral_Migration.xlsx"
df = pd.read_excel(file)
df.head()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
File ~\miniconda3\envs\jupyterbook\lib\site-packages\pandas\compat\_optional.py:135, in import_optional_dependency(name, extra, errors, min_version)
    134 try:
--> 135     module = importlib.import_module(name)
    136 except ImportError:

File ~\miniconda3\envs\jupyterbook\lib\importlib\__init__.py:126, in import_module(name, package)
    125         level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)

File <frozen importlib._bootstrap>:1050, in _gcd_import(name, package, level)

File <frozen importlib._bootstrap>:1027, in _find_and_load(name, import_)

File <frozen importlib._bootstrap>:1004, in _find_and_load_unlocked(name, import_)

ModuleNotFoundError: No module named 'openpyxl'

During handling of the above exception, another exception occurred:

ImportError                               Traceback (most recent call last)
Cell In[2], line 2
      1 file = r"P_Data_Extract_From_Global_Bilateral_Migration.xlsx"
----> 2 df = pd.read_excel(file)
      3 df.head()

File ~\miniconda3\envs\jupyterbook\lib\site-packages\pandas\io\excel\_base.py:495, in read_excel(io, sheet_name, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skiprows, nrows, na_values, keep_default_na, na_filter, verbose, parse_dates, date_parser, date_format, thousands, decimal, comment, skipfooter, storage_options, dtype_backend, engine_kwargs)
    493 if not isinstance(io, ExcelFile):
    494     should_close = True
--> 495     io = ExcelFile(
    496         io,
    497         storage_options=storage_options,
    498         engine=engine,
    499         engine_kwargs=engine_kwargs,
    500     )
    501 elif engine and engine != io.engine:
    502     raise ValueError(
    503         "Engine should not be specified when passing "
    504         "an ExcelFile - ExcelFile already has the engine set"
    505     )

File ~\miniconda3\envs\jupyterbook\lib\site-packages\pandas\io\excel\_base.py:1567, in ExcelFile.__init__(self, path_or_buffer, engine, storage_options, engine_kwargs)
   1564 self.engine = engine
   1565 self.storage_options = storage_options
-> 1567 self._reader = self._engines[engine](
   1568     self._io,
   1569     storage_options=storage_options,
   1570     engine_kwargs=engine_kwargs,
   1571 )

File ~\miniconda3\envs\jupyterbook\lib\site-packages\pandas\io\excel\_openpyxl.py:552, in OpenpyxlReader.__init__(self, filepath_or_buffer, storage_options, engine_kwargs)
    534 @doc(storage_options=_shared_docs["storage_options"])
    535 def __init__(
    536     self,
   (...)
    539     engine_kwargs: dict | None = None,
    540 ) -> None:
    541     """
    542     Reader using openpyxl engine.
    543 
   (...)
    550         Arbitrary keyword arguments passed to excel engine.
    551     """
--> 552     import_optional_dependency("openpyxl")
    553     super().__init__(
    554         filepath_or_buffer,
    555         storage_options=storage_options,
    556         engine_kwargs=engine_kwargs,
    557     )

File ~\miniconda3\envs\jupyterbook\lib\site-packages\pandas\compat\_optional.py:138, in import_optional_dependency(name, extra, errors, min_version)
    136 except ImportError:
    137     if errors == "raise":
--> 138         raise ImportError(msg)
    139     return None
    141 # Handle submodules: if we have submodule, grab parent module from sys.modules

ImportError: Missing optional dependency 'openpyxl'.  Use pip or conda to install openpyxl.

De tweede dataset die we gebruiken is de dataset over de ontwikkeling van landen over tijd aan de hand van levensstandaarden (world Bank Group, 2025). Het succes van een land is hier in veel verschillende methodes af te lezen. Door het grote aantal variabelen hebben we gekozen voor de meest belangrijke variabelen met de grootste hoeveelheid data. Door te kijken naar de stijgingen of dalingen in de dataset kunnen wij concluderen hoe een land zich over een periode van jaren/decennia ontwikkeld. (Link naar de database: https://databank.worldbank.org/source/world-development-indicators)

file2 = r"P_Data_Extract_From_World_Development_Indicators.xlsx"
df = pd.read_excel(file2)
df.head()
Country Name Country Code Series Name Series Code 1960 [YR1960] 1970 [YR1970] 1980 [YR1980] 1990 [YR1990] 2000 [YR2000]
0 Afghanistan AFG Access to clean fuels and technologies for coo... EG.CFT.ACCS.ZS .. .. .. .. 5.5
1 Afghanistan AFG Access to clean fuels and technologies for coo... EG.CFT.ACCS.RU.ZS .. .. .. .. 0.8
2 Afghanistan AFG Access to clean fuels and technologies for coo... EG.CFT.ACCS.UR.ZS .. .. .. .. 25.3
3 Afghanistan AFG Access to electricity (% of population) EG.ELC.ACCS.ZS .. .. .. .. 4.4
4 Afghanistan AFG Access to electricity, rural (% of rural popul... EG.ELC.ACCS.RU.ZS .. .. .. .. ..

De derde dataset die we gebruiken is de dataset met de inwonersaantallen per land. Aan de hand van deze dataset kunnen we de migratiecijfers vergelijken met de inwonersaantallen van het betreffende land. Dit heeft de voordelen dat inwoner aantallen niet een vertekend beeld geven in het onderzoek (Link naar de database: https://data.worldbank.org/indicator/SP.POP.TOTL)

De laatste datasets die gebruikt worden als ondersteuning van onze perspectieven zijn de datasets “bbb.xlsx, gdp_per_inwoner.xlsx en longitude-latitude.xlsx”. bbb.xlsx is een dataset die de sterftecijfers per land weergeeft over een tijdsperiode. Deze hebben we los gemaakt uit world development indicators.

gdp_per_inwoner.xlsx komt uit de World Bank Group (2025) en geeft het gemiddelde bruto binnenlands product (BBP/GDP) aan per inwoner over een tijdsperiode.

Longitude-latitude.xlsx is een dataset die uit Google Datasets(2025) komt. De dataset geeft de coördinaten van alle landen in de wereld aan.

We hebben onze datasets niet echt verwerkt voordat we gingen werken aan de visualisaties. We hebben dit bij elke visualisatie apart gedaan in plaats van in één keer aan het begin. Dit komt vooral omdat we allemaal appart de data hadden verwerkt voor onze eigen visualisaties.

import pandas as pd
import plotly.graph_objects as go


df = pd.read_excel('P_Data_Extract_From_Global_Bilateral_Migration.xlsx')
df.columns = df.columns.str.strip()
coords = pd.read_csv('longitude-latitude.csv')
coords = coords[['ISO-ALPHA-3', 'Latitude', 'Longitude']].rename(columns={'ISO-ALPHA-3': 'Code'}).drop_duplicates()


df = df.dropna(subset=['Country Origin Name', 'Country Origin Code'])
year_columns = [col for col in df.columns if '[' in col and ']' in col]

for year in year_columns:
    df[year] = pd.to_numeric(df[year], errors='coerce').fillna(0)

valid_countries = sorted(df['Country Origin Name'].unique())


fig = go.Figure()

default_country = valid_countries[0] if valid_countries else None
default_year = year_columns[0] if year_columns else None
default_year_str = default_year.split('[')[1].split(']')[0] if default_year else None


for country in valid_countries:
    for year_col in year_columns:
        year = year_col.split('[')[1].split(']')[0]

        country_df = df[df['Country Origin Name'] == country].copy()
        country_df = country_df.rename(columns={year_col: 'Migratie'})
        country_df = country_df.merge(coords, left_on='Country Origin Code', right_on='Code', how='left')
        country_df = country_df.rename(columns={'Latitude': 'Lat_O', 'Longitude': 'Lon_O'}).drop(columns='Code')
        country_df = country_df.merge(coords, left_on='Country Dest Code', right_on='Code', how='left')
        country_df = country_df.rename(columns={'Latitude': 'Lat_D', 'Longitude': 'Lon_D'}).drop(columns='Code')
        country_df = country_df.dropna(subset=['Lat_O', 'Lon_O', 'Lat_D', 'Lon_D'])

        lats, lons, names = [], [], []

        for _, row in country_df.iterrows():
            if float(row['Migratie']) > 0:
                lats.extend([row['Lat_O'], row['Lat_D'], None])
                lons.extend([row['Lon_O'], row['Lon_D'], None])
                names.extend([f"{row['Country Dest Name']} ({int(float(row['Migratie']))})"] * 2 + [None])

        fig.add_trace(
            go.Scattergeo(
                lat=lats,
                lon=lons,
                hovertext=names,
                mode='lines',
                line=dict(width=1, color='red'),
                name=f"{country} ({year})",
                visible=(country == default_country and year == default_year_str)
            )
        )


def make_visibility(selected_country, selected_year):
    visibility = []
    for trace in fig.data:
        trace_country = trace.name.split(' (')[0]
        trace_year = trace.name.split('(')[-1][:-1]
        visibility.append(trace_country == selected_country and trace_year == selected_year)
    return visibility


combo_buttons = []
for country in valid_countries:
    for year_col in year_columns:
        year = year_col.split('[')[1].split(']')[0]
        visibility = make_visibility(country, year)
        combo_buttons.append(
            dict(
                label=f"{country} - {year}",
                method="update",
                args=[
                    {"visible": visibility},
                    {"title": f"Migratiestromen vanuit {country} in {year}"}
                ]
            )
        )


fig.update_layout(
    title=f'Migratiestromen vanuit {default_country} in {default_year_str}',
    geo=dict(
        projection_type='natural earth',
        showland=True,
        landcolor='rgb(243, 243, 243)',
        countrycolor='rgb(204, 204, 204)',
        fitbounds="locations",
        lataxis_range=[-90, 90],
        lonaxis_range=[-180, 180]
    ),
    updatemenus=[
        dict(
            buttons=combo_buttons,
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=1.05,  
            yanchor="top"
        )
    ],
    height=700,
    margin=dict(r=0, t=80, l=0, b=0),
    dragmode=False  
)


fig.show(config={'scrollZoom': False, 'staticPlot': True})

Figuur 1: Een wereldwijde visualisatie van de emigratiestromen (rood) naar landen van bestemming vanuit een enkel land van oorsprong. Deze visualisatie geeft de lezer een inkijk in wereldwijde bewegingspatronen van migranten.

Perspectief 1#

Argument 1: Migratie en Leefomstandigheden, een Historische herhaling#

Wereldwijde migratie is vaak geen vrije keuze maar een beslissing genomen uit nood. Oorlog en landelijk conflict spelen daar vaak een rol bij, maar is dit ook werkelijk zo. Ons perspectief wat wij willen onderzoeken en beargumenten heeft te maken met migratie uit nood door slechte levensomstandigheden. Met het perspectief “Migranten emigreren vooral uit hun eigen land omdat de levensomstandigheden in hun eigen land te slecht/gevaarlijk zijn om een normaal leven in te kunnen hebben.” gaan we samen met de twee centrale datasets van dit onderzoek kijken naar een verband tussen migratie en levensstandaarden.

Een historisch voorbeeld wat ons perspectief zou moeten beargumenteren is bijvoorbeeld de Mexicaanse pesos crisis in 1994 wat voor een grote toename tot migratie bracht(Solano & Massey, 2022). Een indicator die de oorzaken van de crisis blootlegt is de explosieve groei van Mexico’s externe schulden. Deze indicator is tegen de migratiecijfers van Mexico aangelegd en is te zien in het resultaat in de figuur hieronder.

migratie = pd.read_excel("P_Data_Extract_From_Global_Bilateral_Migration.xlsx")
wb_data = pd.read_excel("P_Data_Extract_From_World_Development_Indicators.xlsx")

land = "Mexico"
indicator = "External debt stocks, total (DOD, current US$)"

jaar_kolommen = [col for col in migratie.columns if col.startswith('19') or col.startswith('20')]
jaar_kolommen = sorted(jaar_kolommen, key=lambda x: int(x.split()[0]))
migratie_uit_land = migratie[migratie["Country Origin Name"] == land].copy()
for col in jaar_kolommen:
    migratie_uit_land[col] = pd.to_numeric(migratie_uit_land[col], errors='coerce')

totaal_migratie = migratie_uit_land[jaar_kolommen].sum()
df_migratie = totaal_migratie.reset_index()
df_migratie.columns = ['Jaar', 'Migranten']
df_migratie['Jaar'] = df_migratie['Jaar'].str.extract(r'(\d{4})')[0]
wb_row = wb_data[(wb_data["Country Name"] == land) & 
                 (wb_data["Series Name"] == indicator)].copy()
wb_jaar_kolommen = [col for col in wb_row.columns if col.startswith('19') or col.startswith('20')]
for col in wb_jaar_kolommen:
    wb_row[col] = pd.to_numeric(wb_row[col], errors='coerce')

df_indicator = wb_row[wb_jaar_kolommen].T.reset_index()
df_indicator.columns = ['Jaar', indicator]
df_indicator['Jaar'] = df_indicator['Jaar'].str.extract(r'(\d{4})')[0]
merged = pd.merge(df_migratie, df_indicator, on='Jaar', how='inner')
fig = make_subplots(specs=[[{"secondary_y": True}]])

fig.add_trace(
    go.Bar(
        x=merged['Jaar'],
        y=merged['Migranten'],
        name="Migratie",
        marker_color='blue',
        hovertemplate="Jaar: %{x}<br>Migranten: %{y:,}"
    ),
    secondary_y=False
)

fig.add_trace(
    go.Scatter(
        x=merged['Jaar'],
        y=merged[indicator],
        name=indicator,
        line=dict(color="red", dash="dot"),
        hovertemplate=f"Jaar: %{{x}}<br>{indicator}: %{{y:,.0f}}"
    ),
    secondary_y=True
)

fig.update_layout(
    title=f"Emigratie vanuit {land} vs. {indicator} (Staaf + Lijn)",
    xaxis_title="Jaar",
    hovermode="x unified",
    legend=dict(orientation="h", yanchor="bottom", y=1.02),
    height=600
)

fig.update_yaxes(title_text="Migranten (aantal)", secondary_y=False)
fig.update_yaxes(title_text=indicator, secondary_y=True)

fig.show()

Figuur 2: De visualisatie van de vergelijking van migratiecijfers van Mexico (rood) en de schulden door de overheid in Amerikaanse Dollars (blauw). De schulden van de overheid liepen enorm op tijdens de crisis die Mexico onderging in het jaartal 1994. Deze visualisatie geeft de lezer inzicht in dat de migratie cijfers sterk stegen tussen 1990-2000 door een nationale crisis.

Uit de figuur is voor het land Mexico een duidelijke conclusie te trekken over de cijfers van migratie en de slechte levensomstandigheden van dus bijvoorbeeld een financiële crisis. Alhoewel deze argumentatie voor ons perspectief vrij duidelijk is, is het cruciaal om verder te kijken naar alle migratiecijfers van landen op de wereld en hun levensstandaarden.

Argument 2: Migratie en leefomstandigheden, landen met de meeste migratie#

Zoals eerder beargumenteerd is er sprake van migratie bij historische gebeurtenissen wat de leefomstandigheden van een land omlaag haalt, maar is er ook migratie als het land geen historische gebeurtenis ervaart maar gewoon een gemiddelde slechte leefomstandigheden heeft? Daarom zal er verder worden gekeken naar het verband van de levensstandaarden en de migratiecijfers van de top 20 landen met de meeste migratie over de tijdsperiode van onze datasets. De 20 landen die we gaan gebruiken voor verder onderzoek zijn hieronder in de figuur aangegeven.

%pip install xlrd
Note: you may need to restart the kernel to use updated packages.
ERROR: Invalid requirement: '#Deze': Expected package name at the start of dependency specifier
    #Deze
    ^
mig = pd.read_excel('P_Data_Extract_From_Global_Bilateral_Migration.xlsx')


pop = pd.read_excel('API_SP.POP.TOTL_DS2_en_excel_v2_124779.xlsx', skiprows=3)


pop.columns = pop.columns.map(str).str.strip()


years = ['1960 [1960]', '1970 [1970]', '1980 [1980]', '1990 [1990]', '2000 [2000]']


for year in years:
    mig[year] = pd.to_numeric(mig[year], errors='coerce')
    year_short = year.split()[0]
    if year_short in pop.columns:
        pop[year_short] = pd.to_numeric(pop[year_short], errors='coerce')


fig = go.Figure()
buttons = []

# Create one bar chart per year
for i, year in enumerate(years):
    year_short = year.split()[0]

    if year_short not in pop.columns:
        print(f"⚠️ Skipping {year_short} — not in population data.")
        continue


    mig_year = mig.groupby('Country Origin Name')[year].sum().reset_index()
    pop_year = pop[['Country Name', year_short]].copy()


    merged = pd.merge(mig_year, pop_year, left_on='Country Origin Name', right_on='Country Name', how='left')
    merged = merged.dropna()
    merged = merged[merged[year] > 0]


    merged = merged[merged[year_short] >= 1_000_000]


    merged['% Migrated'] = (merged[year] / merged[year_short]) * 100


    merged = merged[merged['% Migrated'] <= 100]
    
    # Get top 20 countries by % migrated
    top_20 = merged.nlargest(20, '% Migrated').sort_values('% Migrated', ascending=True)

    fig.add_trace(go.Bar(
        y=top_20['Country Name'],
        x=top_20['% Migrated'],
        orientation='h',
        text=top_20['% Migrated'].round(2).astype(str) + '%',
        textposition='auto',
        hoverinfo='text',
        hovertext=[
            f"<b>{row['Country Name']}</b><br>"
            f"Migrants: {row[year]:,}<br>"
            f"Population: {row[year_short]:,}<br>"
            f"% Migrated: {row['% Migrated']:.2f}%"
            for _, row in top_20.iterrows()
        ],
        visible=(i == 0),
        name=year_short,
        marker=dict(
            color=top_20['% Migrated'],
            colorscale='Viridis',
            cmin=0,
            cmax=merged['% Migrated'].quantile(0.95)
    )))


    visibility = [False] * len(years)
    visibility[i] = True
    buttons.append(dict(label=year_short, method='update',
                        args=[{'visible': visibility},
                              {'title': f'Top 20 Countries (>1M pop) by % Migrated ({year_short})'}]))

# Layout
fig.update_layout(
    updatemenus=[dict(
        buttons=buttons,
        direction='right',
        showactive=True,
        x=0.5,
        y=1.2,
        xanchor='center',
        yanchor='top'
    )],
    title='Top 20 Countries (>1M population) by % Population Migrated (1960)',
    xaxis_title='% of Population Migrated',
    yaxis_title='Country',
    height=700,
    margin=dict(l=150, r=50, b=100, t=100, pad=4),
    showlegend=False
)

fig.show()

Figuur 3: Een overzicht van de landen met de meeste migratie op basis van hoeveel procent van de bevolking geëmigreerd is.

Deze 20 landen hierboven aangegeven zullen we vergelijken met drie indicatoren die de ontwikkeling/levensstandaarden van een land kunnen schetsen.

Deze ontwikkeling zijn voornamelijk gefocust op de gezondheid van de burger en zijn als volgt:

“Survival to age 65, male (% of cohort)”, “Mortality rate, adult, female (per 1,000 female adults)”, “Life expectancy at birth, total (years)” Door deze indicatoren in kaart te brengen samen met de migratiecijfers van de landen kunnen we per land een conclusie trekken om vervolgens de eindconclusie over ons perspectief te beantwoorden.

import pandas as pd
from plotly.subplots import make_subplots
import plotly.graph_objects as go

migratie = pd.read_excel("P_Data_Extract_From_Global_Bilateral_Migration.xlsx")
wb_data = pd.read_excel("P_Data_Extract_From_World_Development_Indicators.xlsx")

landen = ["Puerto Rico", "Jamaica", "Albania", "West Bank and Gaza", "Bosnia and Herzegovina", "Ireland", "Georgia", "Armenia", "Trinidad and Tobago", "Moldova", "Kazakhstan", "Jordan", "Azerbaijan", "Kuwait", "Belarus", "Estonia", "Eritrea", "El Salvador", "Lebanon", "Lithuania"]
indicatoren = [
    "Survival to age 65, male (% of cohort)",
    "Mortality rate, adult, female (per 1,000 female adults)",
    "Life expectancy at birth, total (years)"
]

jaar_kolommen = [col for col in migratie.columns if col.startswith('19') or col.startswith('20')]
jaar_kolommen = sorted(jaar_kolommen, key=lambda x: int(x.split()[0]))

fig = make_subplots(specs=[[{"secondary_y": True}]])

traces = []
valid_countries = []

for land in landen:
    migratie_uit_land = migratie[migratie["Country Origin Name"] == land].copy()
    if migratie_uit_land.empty:
        continue
    for col in jaar_kolommen:
        migratie_uit_land[col] = pd.to_numeric(migratie_uit_land[col], errors='coerce')
    totaal_migratie = migratie_uit_land[jaar_kolommen].sum()
    df_migratie = totaal_migratie.reset_index()
    df_migratie.columns = ['Jaar', 'Migranten']
    df_migratie['Jaar'] = df_migratie['Jaar'].str.extract(r'(\d{4})')[0]
    indicator_dfs = []
    for indicator in indicatoren:
        wb_row = wb_data[(wb_data["Country Name"] == land) &
                         (wb_data["Series Name"] == indicator)].copy()
        if wb_row.empty:
            df_empty = pd.DataFrame({'Jaar': df_migratie['Jaar'], indicator: [None]*len(df_migratie)})
            indicator_dfs.append(df_empty)
            continue

        wb_jaar_kolommen = [col for col in wb_row.columns if col.startswith('19') or col.startswith('20')]
        for col in wb_jaar_kolommen:
            wb_row[col] = pd.to_numeric(wb_row[col], errors='coerce')

        df_indicator = wb_row[wb_jaar_kolommen].T.reset_index()
        df_indicator.columns = ['Jaar', indicator]
        df_indicator['Jaar'] = df_indicator['Jaar'].str.extract(r'(\d{4})')[0]
        indicator_dfs.append(df_indicator)


    from functools import reduce
    df_indicator_all = reduce(lambda left,right: pd.merge(left,right,on='Jaar', how='outer'), indicator_dfs)

    merged = pd.merge(df_migratie, df_indicator_all, on='Jaar', how='inner')

    if merged.empty:
        continue

    valid_countries.append(land)

    fig.add_trace(
        go.Scatter(
            x=merged['Jaar'],
            y=merged['Migranten'],
            name=f"Migratie - {land}",
            visible=False,
            hovertemplate="Jaar: %{x}<br>Migranten: %{y:,}",
            line=dict(width=2)
        ),
        secondary_y=False
    )
    traces.append(f"Migratie - {land}")

    for indicator in indicatoren:
        fig.add_trace(
            go.Scatter(
                x=merged['Jaar'],
                y=merged[indicator],
                name=f"{indicator} - {land}",
                visible=False,
                line=dict(dash="dot"),
                hovertemplate=f"Jaar: %{{x}}<br>{indicator}: %{{y:,.2f}}"
            ),
            secondary_y=True
        )
        traces.append(f"{indicator} - {land}")

dropdown_buttons = []

for land in valid_countries:

    visibility = []
    for trace_name in traces:
        if land in trace_name:
            visibility.append(True)
        else:
            visibility.append(False)
    dropdown_buttons.append(
        dict(
            label=land,
            method="update",
            args=[{"visible": visibility},
                  {"title": f"Migratie en indicatoren voor {land}"}]
        )
    )


fig.update_layout(
    title="Migratie en indicatoren per land",
    xaxis_title="Jaar",
    hovermode="x unified",
    legend=dict(
        orientation="v",
        yanchor="top",
        y=1,
        xanchor="left",
        x=1.05,
        bordercolor="Black",
        borderwidth=1
    ),
    updatemenus=[
        dict(
            buttons=dropdown_buttons,
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.5,         
            xanchor="center",  
            y=1,            
            yanchor="top" 
        )
    ],
    height=400,
    width=1100  
)

fig.update_yaxes(title_text="Migranten (aantal)", secondary_y=False)
fig.update_yaxes(title_text="Indicatorwaarde", secondary_y=True)
for i, trace in enumerate(fig.data):
    fig.data[i].visible = "Puerto Rico" in traces[i]



fig.show()

Figuur 4: Een visualisatie van de top 20 landen met de meeste emigratie tov de bevolking samen met drie indicatoren die de leefomstandigheden samenvatten van een land (Zie legenda voor kleur aanduidingen). Het doel van de visualisatie is om de lezer interactief te tonen dat ieder land niet per se een directe connectie heeft tussen migratie en slechte leefomstandigheden en dus deels het tegendeel wordt bewezen van ons perspectief.

Uit de visualisatie hierboven aangegeven kunnen we een paar conclusies trekken. Allereerst zien we dat slechte levensomstandigheden niet altijd een garantie voor verhoging in migratiecijfers zijn, bij Puerto Rico zien we bijvoorbeeld dat het aantal migranten over de jaren heen wisselt samen met de cijfers van de gekozen indicatoren. Dit geeft wel aan dat er een verband is tussen migratie en de veranderingen in de leefomstandigheden en gezondheid van mensen, maar dat heeft niet altijd een stijging van migratie als gevolg.

Uit sommige specifieke gevallen zoals Mexico eerder gegeven is deze conclusie wel te trekken. Een ander specifiek geval waar deze conclusie is te trekken is Ierland, dit land heeft een massale toename in migratie tussen de jaren 1960-1970. Deze massale toename in migratie is net zoals bij Mexico een historische gebeurtenis verbonden, tijdens deze periode heeft zich “The Troubles” afgespeeld in Noord-Ierland waar conflict en onrust het thema was in deze periode(Gilmartin, 2022). Deze historische evenementen die een slechte leefomstandigheid vormen voor de burger van zowel Mexico als Ierland laat zien dat slechte levensomstandigheden wel degelijk uitmaken voor een toename in migratie wereldwijd maar de visualisatie geeft ook het tegendeel van ons perspectief aan namelijk dat slechte leefomstandigheden niet de centrale reden is voor massale migratie wereldwijd.

Zie Linker legenda voor het vervolg van het bestand